package com.tengits.mqttmonitor.rnlibs.rs;

import android.content.Context;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.util.Log;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.hoho.android.usbserial.driver.UsbSerialDriver;
import com.hoho.android.usbserial.driver.UsbSerialProber;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class SerialPortApiModule extends ReactContextBaseJavaModule {

    private final ReactApplicationContext reactContext;
    private final Map<String, SerialPortBase> serialPortsMap = new HashMap<String, SerialPortBase>();

    public SerialPortApiModule(final ReactApplicationContext reactContext) {
        super(reactContext);
        this.reactContext = reactContext;
    }

    @Override
    public String getName() {
        return "SerialPortAPI";
    }

    @ReactMethod
    public void open(final String key, ReadableMap options, Promise promise) {
        try {
            SerialPortBase wrapper = serialPortsMap.get(key);
            if (wrapper != null) {
                wrapper.addRef();
                promise.resolve(key);
                return;
            }
            int baudRate = options.getInt("baudRate");
            int parity = options.getInt("parity");
            int dataBits = options.getInt("dataBits");
            int stopBits = options.getInt("stopBits");
            int type = options.getInt("type");
            int port = options.getInt("port");
            SerialPortBase client = null;
            if(type == 2) {
                client = new UsbSerialPortWrapper(this.reactContext, key, baudRate, parity, dataBits, stopBits, port);
            } else if(type == 1){
                client = new SerialPortWrapper(this.reactContext, key, baudRate, parity, dataBits, stopBits);
            } else {
                client = new SerialPortSimulator(this.reactContext, key, baudRate, parity, dataBits, stopBits);
            }

            // TODO: handle previous value
            serialPortsMap.put(key, client);
            promise.resolve(key);
        } catch (IOException e) {
            System.out.println("error: " + e.getMessage());
            e.printStackTrace();
            promise.reject(null, e.getMessage());
        } catch (Exception e) {
            System.out.println("error: " + e.getMessage());
            e.printStackTrace();
            promise.reject(null, "no permission to read or write this serial port");
        }
    }

    @ReactMethod
    public void send(String key, String hexStr, Promise promise) {
        SerialPortBase wrapper = serialPortsMap.get(key);
        if (wrapper == null) {
            promise.reject(null, "serialport not open");
            return;
        }
        try {
            wrapper.writeHex(hexStr);
            promise.resolve(true);
            // Log.i("serialport", "send: " + hexStr);
        } catch (IOException e) {
            e.printStackTrace();
            promise.reject(null, e.getMessage());
        }
    }

    @ReactMethod
    public void close(String key,boolean isForce, Promise promise) {
        SerialPortBase wrapper = serialPortsMap.get(key);
        if (wrapper == null) {
            promise.reject(null, "serialport not open");
            return;
        }

        boolean isRelease = wrapper.close(isForce);
        if(isRelease||isForce) {
            serialPortsMap.remove(key);
        }
        promise.resolve(true);
    }

    @ReactMethod
    public void getSerialPortClients(Promise promise) {
        WritableArray wa = Arguments.createArray();
        for (String key : serialPortsMap.keySet()) {
            wa.pushString(key);
        }
        promise.resolve(wa);
    }

    @ReactMethod
    public void getSerialPortConfig(String key, Promise promise) {
        SerialPortBase wrapper = serialPortsMap.get(key);
        if (wrapper == null) {
            promise.resolve(null);
            return;
        } else {
            WritableMap config = Arguments.createMap();
            config.putInt("baudRate", wrapper.baudRate);
            config.putInt("parity", wrapper.parity);
            config.putInt("dataBits", wrapper.dataBits);
            config.putInt("stopBits", wrapper.stopBits);
            promise.resolve(config);
        }
    }

    @ReactMethod
    public void getUsbInfo(Promise promise) {
        WritableArray wa = Arguments.createArray();
        try {
            UsbManager usbManager = (UsbManager) reactContext.getSystemService(Context.USB_SERVICE);
            for(UsbDevice v : usbManager.getDeviceList().values())
            {
                WritableMap map = Arguments.createMap();
                map.putString("DeviceName", v.getDeviceName());
                map.putString("ManufacturerName", v.getManufacturerName());
                map.putString("ProductName", v.getProductName());
                map.putString("SerialNumber", v.getSerialNumber());
                map.putInt("deviceId", v.getDeviceId());
                map.putInt("ProductId", v.getProductId());
                map.putInt("VendorId", v.getVendorId());
                map.putInt("Protocol", v.getDeviceProtocol());
                map.putInt("ConfigurationCount", v.getConfigurationCount());
                UsbSerialDriver driver = UsbSerialProber.getDefaultProber().probeDevice(v);
                if(driver != null) {
                    map.putInt("Ports", driver.getPorts().size());
                }
                wa.pushMap(map);
            }
            promise.resolve(wa);
        }catch (Exception e) {
            promise.resolve(wa);
        }
    }

    @ReactMethod
    public void isRunning(String key, Promise promise) {
        SerialPortBase wrapper = serialPortsMap.get(key);
        if (wrapper == null) {
            promise.resolve(false);
        } else {
            promise.resolve(wrapper.isRunning());
        }
    }

    @ReactMethod
    public void addListener(String eventName) {
        // Set up any upstream listeners or background tasks as necessary
    }
    @ReactMethod
    public void removeListeners(Integer count) {
        // Remove upstream listeners, stop unnecessary background tasks
    }
}
